using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using MalwareMultiScan.Backends.Services.Interfaces;

namespace MalwareMultiScan.Backends.Backends.Abstracts
{
    /// <inheritdoc />
    public abstract class AbstractLocalProcessScanBackend : AbstractScanBackend
    {
        private readonly IProcessRunner _processRunner;

        /// <inheritdoc />
        protected AbstractLocalProcessScanBackend(IProcessRunner processRunner)
        {
            _processRunner = processRunner;
        }

        /// <summary>
        /// Regex to extract names of threats.
        /// </summary>
        protected abstract Regex MatchRegex { get; }
        
        /// <summary>
        /// Path to the backend.
        /// </summary>
        protected abstract string BackendPath { get; }
        
        /// <summary>
        /// Parse StdErr instead of StdOut.
        /// </summary>
        protected virtual bool ParseStdErr { get; } = false;
        
        /// <summary>
        /// Throw on non-zero exit code.
        /// </summary>
        protected virtual bool ThrowOnNonZeroExitCode { get; } = true;

        /// <summary>
        /// Get backend process parameters.
        /// </summary>
        /// <param name="path">Path to the temporary file.</param>
        /// <returns>Formatted string with parameters and path.</returns>
        protected abstract string GetBackendArguments(string path);
        
        /// <inheritdoc />
        public override Task<string[]> ScanAsync(string path, CancellationToken cancellationToken)
        {
            var exitCode = _processRunner.RunTillCompletion(BackendPath, GetBackendArguments(path), cancellationToken,
                out var standardOutput, out var standardError);

            if (ThrowOnNonZeroExitCode && exitCode != 0)
                throw new ApplicationException($"Process has terminated with an exit code {exitCode}");

            return Task.FromResult(MatchRegex
                .Matches(ParseStdErr ? standardError : standardOutput)
                .Where(x => x.Success)
                .Select(x => x.Groups["threat"].Value)
                .ToArray());
        }
    }
}